package gui;

import domain.player.Computer;
import domain.player.Disc;
import domain.player.Human;
import domain.player.Player;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;
import domain.player.PlayerType;
import main.AppConstants;

/**
 * SetupGameDialog enables to user to configure a new game. The client can set
 * player names, choose player types (human and/or computer) and choose disc
 * color.
 */
public class SetupGameDialog extends JDialog {

	// Players:
	private Player player1;
	private Player player2;
	// Components to validate data for:
	private Container content = getContentPane();
	//private JPanel
	private Box p1Panel;
	private Box p2Panel;
	private Box p1HPanel;
	private Box p2HPanel;
	private JPanel p1VPanel;
	private JPanel p2VPanel;
	private JPanel discP1;
	private JPanel discP2;
	private JTextField textP1Name;
	private JTextField textP2Name;
	private JRadioButton jcbTypeP1Human;
	private JRadioButton jcbTypeP1Computer;
	private JRadioButton jcbTypeP2Human;
	private JRadioButton jcbTypeP2Computer;

	private JRadioButton jcbDebugOn1;
	private JRadioButton jcbDebugOff1;
	private JRadioButton jcbDebugOn2;
	private JRadioButton jcbDebugOff2;
	private JLabel discImageP1;
	private JLabel discImageP2;
	private JSpinner jspnPly1;
	private JSpinner jspnPly2;
	// Component data:
	private Color player1Color = AppConstants.COLOR_DEFAULT_DISC_1;
	private Color player2Color = AppConstants.COLOR_DEFAULT_DISC_2;
	private String player1Name = "Player 1";
	private String player2Name = "Player 2";
	private PlayerType player1Type = PlayerType.HUMAN;
	private PlayerType player2Type = PlayerType.COMPUTER;

	public SetupGameDialog() {
		createDialog();
	}

	/** Constructor used to fill the components with previous values */
	public SetupGameDialog(Player player1, Player player2) {
		if (player1 != null) {
			player1Name = player1.getName();
			player1Type = player1.getType();
			player1Color = player1.getDiscColor();
		}
		if (player2 != null) {
			player2Name = player2.getName();
			player2Type = player2.getType();
			player2Color = player2.getDiscColor();
		}
		createDialog();
	}

	/** Create the dialog */
	public void createDialog() {
		setTitle(AppConstants.SETUP_GAME_DIALOG_TITLE);
		setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
		setModal(true);

		// Add components:
		content.add(createPlayerPanels());
		addNameFields();

		p1HPanel = Box.createHorizontalBox();
		p1Panel.add(p1HPanel);
		p2HPanel = Box.createHorizontalBox();
		p2Panel.add(p2HPanel);
		p1VPanel = new JPanel(new GridLayout(0,1));
		p1HPanel.add(p1VPanel);
		p2VPanel = new JPanel(new GridLayout(0,1));
		p2HPanel.add(p2VPanel);

		addTypeChooser();

		p1VPanel.add(createPlyPanel(0, player1));
		p2VPanel.add(createPlyPanel(1, player2));

		addDiscColor();
		addDebugChoser();

		content.add(createOKButton(), BorderLayout.SOUTH);

		pack();
		setLocationRelativeTo(null);
		setResizable(false);
		setVisible(true);
	}

	/** Returns the Player object for the given playerNum */
	public Player getPlayer(int playerNum) {
		return playerNum % 2 == 0 ? player2 : player1;
	}

	/** Separate the layout into two columns */
	private JPanel createPlayerPanels() {
		// Titled borders with font:
		Font headerFont = new Font(Font.DIALOG, Font.BOLD, 14);
		TitledBorder borderP1 = new TitledBorder("Player 1");
		borderP1.setTitleFont(headerFont);
		TitledBorder borderP2 = new TitledBorder("Player 2");
		borderP2.setTitleFont(headerFont);

		// Player columns:
		JPanel playerGrid = new JPanel(new GridLayout(0, 2));


		// Player #1:
		//p1Panel = new JPanel(new GridLayout(0, 1));
		p1Panel = Box.createVerticalBox();
		p1Panel.setBorder(borderP1);
		playerGrid.add(p1Panel);

		// Player #2:
		//p2Panel = new JPanel(new GridLayout(0, 1));
		p2Panel = Box.createVerticalBox();
		p2Panel.setBorder(borderP2);
		playerGrid.add(p2Panel);

		return playerGrid;
	}

	/** Add player name componnents to the dialog */
	private void addNameFields() {
		TitledBorder border = new TitledBorder("Name");

		JPanel textP1Panel = new JPanel();
		textP1Panel.setBorder(border);
		textP1Name = new JTextField(player1Name, 25);
		textP1Panel.add(textP1Name);
		p1Panel.add(textP1Panel);

		JPanel textP2Panel = new JPanel();
		textP2Panel.setBorder(border);
		textP2Name = new JTextField(player2Name, 25);
		textP2Panel.add(textP2Name);
		p2Panel.add(textP2Panel);
	}

	/** Add player type componnents to the dialog */
	private void addTypeChooser() {
		TitledBorder typeTitle = new TitledBorder("Type");

		// Player #1:
		Box typePanelP1 = Box.createVerticalBox();
		typePanelP1.setAlignmentY(BOTTOM_ALIGNMENT);
		typePanelP1.setBorder(typeTitle);
		jcbTypeP1Human = new JRadioButton("Human");
		jcbTypeP1Computer = new JRadioButton("Computer");
		// Pre-selected type:
		if (player1Type == PlayerType.HUMAN) {
			jcbTypeP1Human.setSelected(true);
		} else {
			jcbTypeP1Computer.setSelected(true);
		}

		// Group radio buttons:
		ButtonGroup groupTypeP1 = new ButtonGroup();
		groupTypeP1.add(jcbTypeP1Human);
		groupTypeP1.add(jcbTypeP1Computer);

		// Add radio buttons:
		typePanelP1.add(jcbTypeP1Human);
		typePanelP1.add(jcbTypeP1Computer);

		// Player #2:
		Box typePanelP2 = Box.createVerticalBox();
		typePanelP2.setAlignmentY(BOTTOM_ALIGNMENT);
		typePanelP2.setBorder(typeTitle);

		jcbTypeP2Human = new JRadioButton("Human");
		jcbTypeP2Computer = new JRadioButton("Computer");
		jcbTypeP2Computer.setSelected(true);

		// Pre-selected type:
		if (player2Type == PlayerType.HUMAN) {
			jcbTypeP2Human.setSelected(true);
		} else {
			jcbTypeP2Computer.setSelected(true);
		}

		// Group radio buttons:
		ButtonGroup groupTypeP2 = new ButtonGroup();
		groupTypeP2.add(jcbTypeP2Human);
		groupTypeP2.add(jcbTypeP2Computer);

		// Add radio buttons:
		typePanelP2.add(jcbTypeP2Human);
		typePanelP2.add(jcbTypeP2Computer);

		// Add type panels to the player panels:
		p1VPanel.add(typePanelP1);
		p2VPanel.add(typePanelP2);
	}

	/** Add player type componnents to the dialog */
	private void addDebugChoser() {
		TitledBorder typeTitle = new TitledBorder("Debug");

		// Player #1:
		JPanel debugPanelP1 = new JPanel();
		debugPanelP1.setAlignmentY(BOTTOM_ALIGNMENT);
		debugPanelP1.setBorder(typeTitle);
		jcbDebugOn1 = new JRadioButton("On");
		jcbDebugOff1 = new JRadioButton("Off");
		// Pre-selected type:
		jcbDebugOff1.setSelected(true);

		// Group radio buttons:
		ButtonGroup groupDebugP1 = new ButtonGroup();
		groupDebugP1.add(jcbDebugOn1);
		groupDebugP1.add(jcbDebugOff1);

		// Add radio buttons:
		debugPanelP1.add(jcbDebugOn1);
		debugPanelP1.add(jcbDebugOff1);

		// Player #2:
		JPanel debugPanelP2 = new JPanel();
		debugPanelP2.setAlignmentY(BOTTOM_ALIGNMENT);
		debugPanelP2.setBorder(typeTitle);

		jcbDebugOn2 = new JRadioButton("On");
		jcbDebugOff2 = new JRadioButton("Off");
		jcbDebugOff2.setSelected(true);

		// Pre-selected type:
		jcbDebugOff2.setSelected(true);

		// Group radio buttons:
		ButtonGroup groupDebugP2 = new ButtonGroup();
		groupDebugP2.add(jcbDebugOn2);
		groupDebugP2.add(jcbDebugOff2);

		// Add radio buttons:
		debugPanelP2.add(jcbDebugOn2);
		debugPanelP2.add(jcbDebugOff2);

		// Add type panels to the player panels:
		p1Panel.add(debugPanelP1);
		p2Panel.add(debugPanelP2);
	}

	/** Add disc colors */
	private void addDiscColor() {
		TitledBorder titleDisc = new TitledBorder("Disc Color");

		// Player #1:
		discP1 = new JPanel();
		discP1.setBorder(titleDisc);
		p1HPanel.add(discP1);

		// Player #2:
		discP2 = new JPanel();
		discP2.setBorder(titleDisc);
		p2HPanel.add(discP2);

		createDiscIcons();
	}

	/** Update the disc icons */
	private void updateDiscIcons() {
		discImageP1.setIcon(new DiscIcon(player1Color));
		discImageP2.setIcon(new DiscIcon(player2Color));
	}

	/** Add the disc icons */
	private void createDiscIcons() {
		// Player #1:
		discImageP1 = new JLabel();
		discImageP1.setIcon(new DiscIcon(player1Color));
		discImageP1.addMouseListener(new ColorListener(1));
		discP1.add(discImageP1);

		// Player #2:
		discImageP2 = new JLabel();
		discImageP2.setIcon(new DiscIcon(player2Color));
		discImageP2.addMouseListener(new ColorListener(2));
		discP2.add(discImageP2);
	}

	/** Add the OK button component to the dialog */
	private JPanel createOKButton() {
		JPanel buttonPanel = new JPanel(new FlowLayout());
		buttonPanel.setBackground(AppConstants.COLOR_PANEL_BACKGROUND);
		buttonPanel.setBorder(BorderFactory.createMatteBorder(1, 0, 0, 0, AppConstants.COLOR_PANEL_BORDER));

		// OK button:
		JButton jBtnOK = new JButton("OK");
		jBtnOK.setBackground(AppConstants.COLOR_PANEL_BACKGROUND);
		jBtnOK.addActionListener(new ActionListener() {

			/**
			 * Button listener: OK
			 * If there's valid data = create players otherwise show error dialog
			 */
			@Override
			public void actionPerformed(ActionEvent e) {
				if (isValidInput()) {
					createPlayers();
					dispose();
				} else {
					new ErrorDialog("Enter correct values!");
				}
			}
		});
		buttonPanel.add(jBtnOK);

		return buttonPanel;
	}

	/** Check whether or not the input is valid */
	public boolean isValidInput() {
		return isValidNames() && isValidTypes();
	}

	/** Validate names */
	public boolean isValidNames() {
		player1Name = textP1Name.getText().trim();
		player2Name = textP2Name.getText().trim();

		return (!player1Name.equals("") && !player2Name.equals(""));
	}

	/** Validate types */
	public boolean isValidTypes() {
		player1Type = (jcbTypeP1Human.isSelected() ? PlayerType.HUMAN
				: (jcbTypeP1Computer.isSelected() ? PlayerType.COMPUTER
				: null));
		player2Type = (jcbTypeP2Human.isSelected() ? PlayerType.HUMAN
				: (jcbTypeP2Computer.isSelected() ? PlayerType.COMPUTER
				: null));

		return (player1Type != null && player2Type != null);
	}

	/** Create players */
	public void createPlayers() {
		boolean debug;
		player1 = player1Type == PlayerType.HUMAN
				? new Human(player1Name, Disc.ONE, player1Color)
				: new Computer(player1Name, Disc.ONE, player1Color);
		player2 = player2Type == PlayerType.HUMAN
				? new Human(player2Name, Disc.TWO, player2Color)
				: new Computer(player2Name, Disc.TWO, player2Color);

		if(player1.getType() == PlayerType.COMPUTER){
			((Computer)player1).setPly((Integer) jspnPly1.getValue());
			((Computer)player1).setDebug(jcbDebugOn1.isSelected());
		}

		if(player2.getType() == PlayerType.COMPUTER){
			((Computer)player2).setPly((Integer) jspnPly2.getValue());
			((Computer)player2).setDebug(jcbDebugOn2.isSelected());
		}
	}

	/** Color listener */
	private class ColorListener implements MouseListener {

		private int playerNum;

		public ColorListener(int playerNum) {
			this.playerNum = playerNum;
		}

		@Override
		public void mouseClicked(MouseEvent e) {
			JColorChooser colorChooser = new JColorChooser();
			Color selectedColor = colorChooser.showDialog(content,
					"Choose disc color",
					(playerNum % 2 == 0 ? player2Color : player1Color));

			if (selectedColor == null) {
				return;
			}

			if (playerNum % 2 == 0) {
				player2Color = selectedColor;
			} else {
				player1Color = selectedColor;
			}
			// Update the disc icons with the new color:
			updateDiscIcons();
		}

		@Override
		public void mousePressed(MouseEvent e) {
		}

		@Override
		public void mouseReleased(MouseEvent e) {
		}

		@Override
		public void mouseEntered(MouseEvent e) {
		}

		@Override
		public void mouseExited(MouseEvent e) {
		}
	}

    private class SetupBoardListener implements ActionListener {
            public SetupBoardListener(){

            }

		//TODO: remove this.
        @Override
        public void actionPerformed(ActionEvent e) {
            new SetDialog(null);
        }


        }

	/** Creates a JPanel with a spinner for changing how many moves ahead the computer will look */
	private JPanel createPlyPanel(int index, Player p) {
		SpinnerModel searchModel;
		JPanel pane = new JPanel();

		pane.setBorder(new TitledBorder(" Ply "));

		if(p != null && p.getType() == PlayerType.COMPUTER)
			searchModel = new SpinnerNumberModel(((Computer)p).getMovesAhead(), 0, 15, 1);
		else
			searchModel = new SpinnerNumberModel(7, 0, 15, 1);

		JSpinner spinner = new JSpinner(searchModel);
		JLabel label = new JLabel("Ply (0-15): ");
		label.setLabelFor(spinner);

		pane.add(label);
		pane.add(spinner);

		if (index == 0) {
			jspnPly1 = spinner;
		} else {
			jspnPly2 = spinner;
		}

		return pane;
	}
}